package jblip.base.resources;

import java.text.DateFormat;
import java.text.ParseException;
import java.util.Date;
import java.util.EnumMap;
import java.util.Map;
import java.util.Vector;

import jblip.resources.Media;
import jblip.resources.PathResource;
import jblip.resources.ResourcesFactory;
import jblip.resources.Shortlink;
import jblip.resources.Subscription;
import jblip.resources.Transport;
import jblip.resources.Update;
import jblip.resources.UpdateType;
import jblip.resources.User;
import jblip.resources.UserPicture;
import jblip.resources.Media.MediaType;
import jblip.resources.UserPicture.PictureSize;
import jblip.util.json.JSONObject;

/**
 * Default implementation of resources factory.
 * 
 * <p>
 * The input objects of methods in this class should be a textual
 * representations of JSON objects returned by the Blip system in a HTTP
 * response body.
 * </p>
 * 
 * <p>
 * This class is based on 0.02 version of the Blip system API.
 * </p>
 * 
 * @author Krzysztof Sroka
 * @since 0.1
 */
public class DefaultResourceFactory implements ResourcesFactory {

  @Override
  public BaseMedia getMedia(Object object, MediaType type) {
    if (object instanceof JSONObject) {
      JSONObject json = (JSONObject) object;
      String url = json.getString("url");
      Integer id = json.getInteger("id");
      BaseMedia media = new BaseMedia(id, url, type);
      PathResource<Update> update = PathResource.getUpdate(json, "update");
      if (update.hasResource()) {
        media.setUpdate(update.getResource(this));
      } else {
        media.setUpdatePath(update.getPath());
      }
      return media;
    } else {
      return null;
    }
  }

  @Override
  public Transport getTransport(Object object) {
    if (object instanceof JSONObject) {
      JSONObject json = (JSONObject) object;
      int id = json.getInteger("id");
      Transport std = Transport.getStandardTransport(id);
      if (std != null) {
        return std;
      } else {
        return new BaseTransport(id, json.getString("name"));
      }
    } else {
      return null;
    }
  }

  @Override
  public BaseUpdate getUpdate(Object object) {
    if (object instanceof JSONObject) {
      JSONObject json = (JSONObject) object;
      String body = json.getString("body");
      Integer id = json.getInteger("id");
      UpdateType type = BaseUpdate.getUpdateType(json.getString("type"));
      Transport transport = getTransport(json.getJSONObject("transport"));
      Date created_at;
      try {
        created_at = DateFormat.getDateTimeInstance().parse(
                json.getString("created_at"));
      } catch (ParseException e) {
        created_at = null;
      }

      BaseUpdate upd = new BaseUpdate(id, body, type, transport);
      upd.setCreatedAt(created_at);

      PathResource<Vector<Media>> pics = PathResource.getMediaVector(json,
              "pictures", MediaType.PICTURE);
      if (pics.hasResource()) {
        for (Media pic : pics.getResource(this)) {
          upd.addPicture(pic);
        }
      } else {
        upd.setAttachedPicturesPath(pics.getPath());
      }

      PathResource<User> dst_user = PathResource.getUser(json, "recipient");
      if (dst_user.hasResource()) {
        upd.setDstUser(null, dst_user.getResource(this));
      } else if (type.hasRecipient()) {
        String dst_login = dst_user.getPath().substring(
                dst_user.getPath().lastIndexOf('/') + 1);
        upd.setDstUser(dst_login, null);
      }

      PathResource<User> src_user = PathResource.getUser(json, "user");
      if (src_user.hasResource()) {
        upd.setSrcUser(null, src_user.getResource(this));
      } else {
        String src_login = src_user.getPath().substring(
                src_user.getPath().lastIndexOf('/') + 1);
        upd.setSrcUser(src_login, null);
      }

      PathResource<Media> rec = PathResource.getMedia(json, "recording",
              MediaType.RECORDING);

      if (rec.hasResource()) {
        upd.setAttachedRecording(rec.getResource(this));
      } else {
        upd.setAttachedRecordingPath(rec.getPath());
      }

      PathResource<Media> movie = PathResource.getMedia(json, "movie",
              MediaType.MOVIE);
      if (movie.hasResource()) {
        upd.setAttachedMovie(movie.getResource(this));
      } else {
        upd.setAttachedMoviePath(movie.getPath());
      }

      return upd;
    } else {
      return null;
    }
  }

  @Override
  public BaseUser getUser(Object object) {
    if (object instanceof JSONObject) {
      JSONObject json = (JSONObject) object;
      int uid = json.getInteger("id");
      String login = json.getString("login");
      BaseUser user = new BaseUser(uid, login);
      PathResource<UserPicture> avatar = PathResource.getUserPicture(json,
              "avatar");
      PathResource<Media> bg = PathResource.getMedia(json, "background",
              MediaType.PICTURE);
      if (avatar.hasResource()) {
        user.setAvatar(avatar.getResource(this));
      } else {
        user.setAvatarPath(avatar.getPath());
      }
      if (bg.hasResource()) {
        user.setBackgroundPicture(bg.getResource(this));
      } else {
        user.setBackgroundPath(bg.getPath());
      }
      if (json.contains("location")) {
        user.setLocation(json.getString("location"));
      }
      user.setSex(json.getString("sex"));
      return user;
    } else {
      return null;
    }
  }

  @Override
  public UserPicture getUserPicture(Object object) {
    if (object instanceof JSONObject) {
      JSONObject json = (JSONObject) object;
      int id = json.getInteger("id");
      Map<PictureSize, String> urls =
              new EnumMap<PictureSize, String>(PictureSize.class);
      urls.put(PictureSize.FEMTO, json.getString("url_15"));
      urls.put(PictureSize.NANO, json.getString("url_30"));
      urls.put(PictureSize.PICO, json.getString("url_50"));
      urls.put(PictureSize.STANDARD, json.getString("url_90"));
      urls.put(PictureSize.DEFAULT, json.getString("url"));
      urls.put(PictureSize.LARGE, json.getString("url_120"));

      return new BaseUserPicture(id, urls);
    }
    return null;
  }

  @Override
  public Shortlink getShortlink(Object object) {
    if (object instanceof JSONObject) {
      JSONObject json = (JSONObject) object;
      int id = json.getInteger("id");
      int hit_count = json.getInteger("hit_count");
      Date created_at = null;
      try {
        created_at = DateFormat.getDateTimeInstance().parse(
                json.getString("created_at"));
      } catch (ParseException e) {
        return null;
      }
      String shortcode = json.getString("shortcode");
      String original_link = json.getString("original_link");
      return new BaseShortlink(id, hit_count, created_at, shortcode,
              original_link);
    } else {
      return null;
    }
  }

  @Override
  public Subscription getSubscription(Object object) {
    if (object instanceof JSONObject) {
      JSONObject obj = (JSONObject) object;
      Transport t = getTransport(obj.getJSONObject("transport"));
      PathResource<User> ted = PathResource.getUser(obj, "tracked_user");
      PathResource<User> ting = PathResource.getUser(obj, "tracking_user");
      return new BaseSubscription(t, ted.getPath(), ting.getPath(), ted.
              getResource(this), ting.getResource(this));
    } else {
      return null;
    }
  }
}
